28 - Query Multiple Elements

之前学习了8个查询单个元素的API,现在开始学习查询多个元素的API。以getAllBy开头的api,如果查找到了,则返回数组;否则报错。

image-20251111161603532

所有getBy开头的API,以getAllBy开头都有:

image-20251111161818954

我们现在只需要关注getAllByRole这个API,其余的可以自行学习。

创建组件Skills:

编写测试文件:

测试OK。

29 - TextMatch

image-20251111163341722

这是我们之前写的查询代码,这些查询API的第一个参数,看上去好像是第一个参数必须是string类型,其实不是。

第一个参数是TextMatch类型,可以是string、regex或者是一个函数。

image-20251111163545110

1、当TextMatch是string类型时

image-20251111164346594

2、当TextMatch是regex时

image-20251111164742985

3、当TextMatch是function时

function应该按照下面这种ts类型来写

image-20251111164919700

30 - queryBy

如果想要测试没有出现在DOM中的代码,该怎么做呢?

比如说我使用isLoggedIn来条件显示代码,怎么测没有出现在DOM中的代码呢?

此时使用getBy前缀的方法,是拿不到<button>Start Learning</button>这个DOM的,所以即使我是这样来测试(.not.toBeInTheDocument()),也会报错:

image-20251112141033163

这时候就可以使用queryBy...或者queryAllBy...前缀开头的API,getBy...开头的API它们都有相应的。

image-20251112141210507

queryBy... 最常用于测试组件中的 条件渲染(Conditional Rendering),即检查某个元素在特定条件下应该被移除。

queryAllBy... 最常用于测试列表或重复元素,以确认在某种状态下(如列表为空)页面上没有任何该类型的元素出现。

那么测试代码就可以使用queryByRole

测试OK:

image-20251112141649578

 

image-20251112141611252

31 - findBy

我们之前已经学了:

image-20251112141828999

当你的组件在渲染后不会立即显示某个元素,而需要等待异步操作(如 API 调用定时器状态更新)完成后才显示时,该怎么办?

findByfindAllBy 是 React Testing Library (RTL) 中专用于 异步操作 的查询方法。

它们的作用是:在一段等待时间(默认 1000ms 或可配置)内,持续地(45ms 的间隔 内,反复执行查询)查找一个元素,直到找到它,或者超时失败。返回结果是一个 Promise

image-20251112141905077

在Skills.tsx里面,添加延时操作,0.5s后显示Start learning这个button。

image-20251112142323637

使用findByRole方法:

测试OK:

image-20251112143316221

下面是findBy...findAllBy...开头的API的介绍。getBy...开头的API,findBy...findAllBy...都有。

image-20251112143036703

32 - Manual Queries

虽然可以使用之前学过的JS DOM API来获取元素,但是不推荐使用这些方法来测试。还是使用RTL Queries提供的三种类型的方法。

image-20251112143941289

33 - Debugging

如果测试代码里面有错误,terminal里面的信息也是很详细的。比如说我把button的内文本写错了:

image-20251112144505293

terminal里面的信息给出了提示:

image-20251112144440796

还有两种方法,能够帮助我们进行调试。

1、screen.debug()

作用:将当前在 jsdom 中渲染的整个 DOM 结构(或指定的元素)以易于阅读的字符串形式输出到控制台(console)。

image-20251112144706275

可以看到,两处的dom树结构不一样,这是因为中间使用了promise查询。

image-20251112144728318

2、logRoles方法

作用:扫描当前渲染的 DOM 结构,并将所有元素及其对应的 WAI-ARIA 角色(Role)和可访问性名称(Accessible Name)打印到控制台。

logRoles() 是 RTL 核心哲学 的完美体现:鼓励开发者通过 角色名称 来查询元素。

查找正确的 Role: 当你想使用 getByRole 进行查询,但不确定一个元素到底被识别为哪个角色时(例如,一个自定义组件),logRoles() 可以告诉你准确的角色名称。

验证 Accessible Name: 确保你的交互元素(按钮、链接、输入框)具有正确的 可访问性名称。如果名称不正确,getByRolegetByLabelText 就会失败。

发现 ARIA 错误: 它可以帮助你发现哪些元素缺少必要的 ARIA 属性,从而改进组件的可访问性。

调试查询失败:getByRole 失败时,运行 logRoles() 可以直接告诉你这个元素的可访问性名称是什么,你只需将这个名称填入 getByRolename 选项即可。

引入:

使用:

image-20251112145302374

输出:

image-20251112145325660

可以看到,会输出元素对应的role属性值和Name值。

34 - Testing Playground

这个插件已经没有了,不用管了。

image-20251112150341439

35 - User Interactions

测试同样需要保证用户交互符合预期。

image-20251112150717981

需要使用user-event库。安装:npm i @testing-library/user-event。所有user event的API都是异步的。

image-20251112150905752

虽然RTL里面有fireEvent可以模拟用户操作,但是user-event更牛逼。

image-20251112151048339

36 - Pointer Interactions

这节课学习测试鼠标点击事件。在 @testing-library/user-event 中,"Pointer Interactions"(指针交互)API 涵盖了所有与 鼠标、触摸板或触摸屏 相关的操作。

这些方法旨在模拟用户与屏幕上的元素进行指向和点击的真实行为。

创建组件:

编写测试文件:

测试OK:

image-20251112153718081

🖱️ 核心指针交互 API

这些API经常使用。

image-20251112152205368

API描述用途
userEvent.click(element, options)模拟完整的 单击 流程 (pointerDown, pointerUp, click 事件链)。最常用的方法,用于激活按钮、链接等。
userEvent.dblClick(element, options)模拟完整的 双击 流程。用于需要双击才能触发功能的元素。
userEvent.tripleClick(element, options)模拟完整的 三击 流程。较少用,但用于特殊文本选择功能等。
userEvent.hover(element, options)模拟鼠标指针悬停在元素上 (pointerOver, mouseOver, pointerEnter, mouseEnter 等)。测试 Tooltip(工具提示)或下拉菜单的显示。
userEvent.unhover(element, options)模拟鼠标指针离开元素 (pointerOut, mouseOut, pointerLeave, mouseLeave 等)。测试 Tooltip 或下拉菜单的隐藏。

👇 低级指针 API (用于复杂交互)

以下 API 用于模拟更底层的指针事件,通常用于测试拖放、长按等复杂或自定义的交互逻辑。这些API不经常使用。

image-20251112152302798

37 - Keyboard Interactions

这节课学习键盘相关的交互API。

修改Count组件代码,添加input和button。

编写键盘交互测试代码:

image-20251112160731452

测试OK:

image-20251112160745889

 

核心键盘交互API

image-20251112155123135

 

image-20251112155150042

 

image-20251112155221502

image-20251112155251409

image-20251112155323385

 

image-20251112155352723

低级指针API(用于复杂交互)

image-20251112160009813

38 - Providers

接下来我们学习怎么测试Provider里面的组件,后面几节课会学习测试自定义react hooks。

我们使用mui的theme作为provider。

1、安装:npm install @mui/material @emotion/react @emotion/styled

2、创建provider文件:

3、创建mui-mode组件,并在App.tsx里面使用它

mui-mode组件的意思就是:当设置了provider的mode之后,mui-mode组件会根据provider的值,展示不同的文字。dark mode或者light mode。

4、编写测试文件

重点就是怎么编写matcher里面的值。如果写成这样.toHaveTextContent("dark mode");,会报错。

image-20251112162556159

查看报错信息,可以看到预期是dark mode,但是实际上是light mode。可是我明明provider里面设置的是dark啊。

这是因为在MuiMode组件里面,并没有AppProvider。我们是在App.tsx里面使用的AppProvider,所以当render(<MuiMode />)的时候,接收不到设置的provider。

解决办法:给render方法传递第二个参数,设置wrapper的值为AppProviders。这个意思是在render MuiMode组件之前,使用AppProviders包裹它。

测试OK:

image-20251112163055844

也许你会想,在App.tsx中,我们可能会使用到很多Providers,那里面的组件测试时都要先包裹住这些Providers吗?太复杂了吧。有没有一次性定义的方式,让我们在组件测试的时候能不用写wrapper呢?

有,下节课会讲到。

39 - Custom Render Functions

上节课我们提出了一个疑问,既然provider里面的组件都使用到了这个provider,而且provider可能有多个,那岂不是要在每一个测试用例里面写wrapper参数?

testing library提供了一个解决办法:https://testing-library.com/docs/react-testing-library/setup

image-20251112170037439

编写一个test-utils.tsx的文件,在里面封装自定义的render,这样以后使用render或其它testing-library/react的API,就可以从这个文件引入。

这样测试OK:

image-20251112171220484

40 - Custom React Hooks

这节课学习怎么测试自定义hooks。

1、创建自定义hook

2、创建测试文件

测试hook不能使用render,要使用renderHook方法。renderHook 的作用就是:

核心属性:

属性作用
result一个对象,包含 Hook 返回的最新值
result.current包含 Hook 实际返回的对象或值(例如 { count, increment })。
rerender(newProps)允许你更新 Hook 的 props,模拟父组件重新渲染。
unmount()模拟组件卸载,用于测试 useEffect 的清理函数。

测试代码:

测试OK:

image-20251112182150821

 

renderHook的initialProps参数:

image-20251112181920660

41 - Act Utility

上节课学习了怎么测试一个react hook的初始值是否符合预期。这节课学习测试hook里面定义的方法。

在写测试代码时,调用hook里面的函数,需要写在act()里面进行调用,这是约定。

image-20251112183108451

测试OK:

image-20251112183120228

42 - Mocking Functions

这节课开始学习mock测试。使用vitestfn方法,vi.fn()

1、创建组件

可以看到,数据是以Props的形式传递过去的。

2、编写测试代码

重点是函数怎么测试,函数只写出了ts类型,但是该怎么测才是正确的方式呢?需要测这些:

如果以后写了组件,不知道该测什么,直接问AI。

测试OK:

image-20251112190103235

43 - Mocking HTTP Requests

这节课学习mock http请求。

1、创建组件

2、编写测试代码

应该测什么呢?

如果数据请求失败,就显示error信息。如果数据请求成功,就显示users信息。

那么测试里面不需要真正请求原始的接口,只需要使用mock请求接口即可。使用https://mswjs.io/提供的工具。下节课会讲到。

44 - MSW Setup

这节课我们知道了Mock Http请求需要用到msw,这节课我们来学习设置它。直接参考mswjs.io的文档:https://mswjs.io/docs/quick-start

1、安装依赖npm i msw --save-dev

2、创建请求处理函数

下节课会讲到。

3、流程级集成

这一步就是配置服务器。

45 - MSW Handlers

创建处理函数:

46 - Testing with MSW

为了能在测试的时候,告诉测试页面使用mock接口,需要在setupTests.ts里面,加上这些代码:

注意:MSW是没有管jestdom相关的测试的,所以它单独写了一个vitest.setup.ts的文件。我只需要将它的代码添加到setupTests.ts里面去就行了。

image-20251112194059403

接下来就可以写测试代码了:

测试OK:

image-20251112195554204

47 - MSW Error Handling

之前的测试,还没有覆盖接口请求error的情况,这节课来完善。

问题:怎么模拟接口请求错误的情况呢?

使用 server.use() 只在当前测试中替换某个请求的响应,其他测试依然走正常逻辑。

测试OK:

image-20251112200955636

 

这个替换掉的请求,会不会影响正常的请求呢?

不会,因为在superTests.ts里面,我们已经加了afterEach(() => server.resetHandlers()),这句话说明每次请求之后,会重置handlers,所以不会影响。

48 - Static Analysis Testing

静态分析测试。

不需要运行代码,就可以检查你的代码是否符合以下期望。

image-20251112201120095

静态测试分析这些方面:代码可读性、持续性、错误处理、类型检查、是否符合最佳实践。总之,就是检查代码写的好不好。

image-20251112201225568

 

主要有5种工具,其中typescript已经加到项目里面了,一直在用,所以学习剩下的四个。

image-20251112201311968

49 - Eslint

eslint:“代码的语法警察 + 质量守门员”。它不运行代码,而是“读懂”代码,帮你提前发现问题。

image-20251112202414908

ESLint 的 6 大核心作用

作用说明举例
1. 发现潜在 Bug找出运行时才会暴露的错误if (user = null) → 赋值写成了相等
2. 强制代码风格统一所有人写法一致' vs "、分号、缩进
3. 提升可维护性减少“奇技淫巧”禁止 eval()、with
4. 强制最佳实践特别是 React Hooks 规则useEffect 依赖数组漏写
5. 自动化 Code Review工具先审,人再审PR 前自动标记问题
6. 与编辑器集成,实时提示写代码时红线提醒VS Code 实时高亮错误

因为项目自带了eslint,所以vscode里面要安装eslint插件来帮助我们。

针对测试,可以安装npm i -D eslint-plugin-jest-dom,来帮助我们分析自己写的jest-dom相关的代码。需要同时配置eslint.config.js,使用的时候找资料即可。

50 - Prettier

prettier是格式化代码工具,可配置。

image-20251112203535177

使用npm i -D --exact prettier,会安装最新稳定版本的prettier,并且锁定到精确版本号(不会用 ^ 允许小版本升级)。如果要更精准的,那么可以在后面加上具体的版本号prettier@3.3.3

因为prettier的版本号不同,格式化的风格也会不同,这样一个团队里面的格式化效果就会有区别。

在package.json里面加一条命令:

忽略掉.gitignore里面不上传到git的文件,然后--write自动修复并保存这些后缀名的文件。

可以使用vscode里面的prettier插件,配置format on save,这样每次保存文件的时候就会格式化,避免每次都要运行命令。

如果eslint和prettier有冲突,可以安装npm i -D eslint-config-prettier,并配置即可。

51 - Husky

image-20251112205721031

Husky 是一个 Git Hooks 工具,让你可以在 git commit、git push 等 Git 操作时,自动运行脚本(比如:格式化代码、运行测试、代码检查)。

安装:npm install -D husky

初始化husky:npx husky init,自动创建.husky/pre-commit文件。

配置pre-commit脚本:

52 - lint-staged

上面使用husky,可以在进行git操作之前,帮助我们格式化、eslint等操作。但是husky会检查整个项目,而我们只需要它帮我们检查修改的文件,怎么办到呢?

使用lint-staged。

image-20251112210433034

lint-staged是一个工具,让你在 git commit 时,只对「被修改的文件(staged files)」运行 lint、format、test 等命令。

1、安装npm install -D lint-staged

2、在package.json里面添加配置

3、修改pre-commit脚本,现在只需要运行lint-staged即可

4、如果想在git push之前运行测试,可以这样配置

创建 pre-push 脚本文件,运行命令npx husky add .husky/pre-push "npm run test",编辑pre-push文件。

53 - Wrapping Up

section1:介绍了什么是测试?

section2:介绍了jest相关内容

section3-4:介绍了RTL相关内容

section5:怎么测试交互

section6:怎么测试provider相关内容,怎么测试hooks

section7:怎么使用mock进行测试

section8:学习静态分析测试